// Project: Salient
// http://salient.codeplex.com
// 
// Copyright 2010, Sky Sanders <sky at skysanders.net>
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://salient.codeplex.com/license
// 
// Date: April 24 2010 

#region

using System;
using System.ComponentModel;
using System.Data.SqlTypes;
using System.IO;
using Microsoft.SqlServer.Types;

#endregion

namespace Salient.SqlServer.Types
{
    ///<summary>
    ///</summary>
    [Serializable]
    [TypeConverter(typeof (HierarchyIdTypeConverter))]
    public class HierarchyId : INullable, IComparable
    {
        private static readonly HierarchyId SmNull;

        private SqlHierarchyId _id;

        static HierarchyId()
        {
            SmNull = new HierarchyId();
        }

        ///<summary>
        ///</summary>
        public HierarchyId()
        {
        }

        ///<summary>
        ///</summary>
        ///<param name="id"></param>
        public HierarchyId(String id)
        {
            _id = SqlHierarchyId.Parse(id);
        }

        ///<summary>
        ///</summary>
        ///<param name="id"></param>
        public HierarchyId(SqlHierarchyId id)
        {
            _id = id;
        }

        ///<summary>
        ///</summary>
        public SqlHierarchyId Inner
        {
            get { return _id; }
        }

        ///<summary>
        ///</summary>
        public static HierarchyId Null
        {
            get { return SmNull; }
        }

        #region IComparable Members

        /// <summary>
        ///                     Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
        ///                 </summary>
        /// <returns>
        ///                     A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: 
        ///                 
        ///                     Value 
        ///                 
        ///                     Meaning 
        ///                 
        ///                     Less than zero 
        ///                 
        ///                     This instance is less than <paramref name="obj" />. 
        ///                 
        ///                     Zero 
        ///                 
        ///                     This instance is equal to <paramref name="obj" />. 
        ///                 
        ///                     Greater than zero 
        ///                 
        ///                     This instance is greater than <paramref name="obj" />. 
        ///                 </returns>
        /// <param name="obj">
        ///                     An object to compare with this instance. 
        ///                 </param>
        /// <exception cref="T:System.ArgumentException"><paramref name="obj" /> is not the same type as this instance. 
        ///                 </exception>
        /// <filterpriority>2</filterpriority>
        public int CompareTo(object obj)
        {
            if (!(obj is HierarchyId))
            {
                throw new ArgumentException(
                    /*string.Format(ordpathres.Culture, ordpathres.ArgumentNotSqlHierarchyId, new object[0])*/);
            }
            return Inner.CompareTo(((HierarchyId) obj).Inner);
        }

        #endregion

        #region INullable Members

        /// <summary>
        ///                     Indicates whether a structure is null. This property is read-only.
        ///                 </summary>
        /// <returns><see cref="T:System.Data.SqlTypes.SqlBoolean" />true if the value of this object is null. Otherwise, false.
        ///                 </returns>
        public bool IsNull
        {
            get { return (_id.IsNull); }
        }

        #endregion

        ///<summary>
        ///</summary>
        ///<returns></returns>
        public static HierarchyId GetRoot()
        {
            SqlHierarchyId root = SqlHierarchyId.GetRoot();
            HierarchyId id = new HierarchyId(root);
            return id;
        }


        ///<summary>
        ///</summary>
        ///<param name="child1"></param>
        ///<param name="child2"></param>
        ///<returns></returns>
        public HierarchyId GetDescendant(HierarchyId child1, HierarchyId child2)
        {
            if (IsNull)
            {
                return Null;
            }

            return new HierarchyId(_id.GetDescendant(child1.Inner, child2.Inner));
        }


        ///<summary>
        ///</summary>
        ///<param name="parent"></param>
        ///<returns></returns>
        public SqlBoolean IsDescendantOf(HierarchyId parent)
        {
            if (!IsNull && !parent.IsNull)
            {
                return _id.IsDescendantOf(parent.Inner);
            }

            return SqlBoolean.Null;
        }


        ///<summary>
        ///</summary>
        ///<returns></returns>
        public SqlInt16 GetLevel()
        {
            if (IsNull)
            {
                return SqlInt16.Null;
            }
            return (short) _id.GetLevel();
        }


        ///<summary>
        ///</summary>
        ///<param name="n"></param>
        ///<returns></returns>
        public HierarchyId GetAncestor(int n)
        {
            if (IsNull)
            {
                return Null;
            }
            return new HierarchyId(_id.GetAncestor(n));
        }


        ///<summary>
        ///</summary>
        ///<param name="oldRoot"></param>
        ///<param name="newRoot"></param>
        ///<returns></returns>
        public HierarchyId GetReparentedValue(HierarchyId oldRoot, HierarchyId newRoot)
        {
            if ((IsNull || oldRoot.IsNull) || newRoot.IsNull)
            {
                return Null;
            }
            return new HierarchyId(_id.GetReparentedValue(oldRoot.Inner, newRoot.Inner));
        }

        ///<summary>
        ///</summary>
        ///<param name="input"></param>
        ///<returns></returns>
        public static HierarchyId Parse(SqlString input)
        {
            if (input.IsNull)
            {
                return Null;
            }
            return new HierarchyId(SqlHierarchyId.Parse(input));
        }

        ///<summary>
        ///</summary>
        ///<param name="hid"></param>
        ///<returns></returns>
        public int CompareTo(HierarchyId hid)
        {
            return Inner.CompareTo(hid.Inner);
        }

        /// <summary>
        ///                     Determines whether the specified <see cref="T:System.Object" /> is equal to the current <see cref="T:System.Object" />.
        ///                 </summary>
        /// <returns>true if the specified <see cref="T:System.Object" /> is equal to the current <see cref="T:System.Object" />; otherwise, false.
        ///                 </returns>
        /// <param name="obj">
        ///                     The <see cref="T:System.Object" /> to compare with the current <see cref="T:System.Object" />. 
        ///                 </param>
        /// <exception cref="T:System.NullReferenceException">
        ///                     The <paramref name="obj" /> parameter is null.
        ///                 </exception>
        /// <filterpriority>2</filterpriority>
        public override bool Equals(object obj)
        {
            if (!(obj is HierarchyId) && !(obj is SqlHierarchyId))
            {
                return false;
            }
            return obj.ToString() == ToString();
        }

        /// <summary>
        ///                     Serves as a hash function for a particular type. 
        ///                 </summary>
        /// <returns>
        ///                     A hash code for the current <see cref="T:System.Object" />.
        ///                 </returns>
        /// <filterpriority>2</filterpriority>
        public override int GetHashCode()
        {
            return Inner.GetHashCode();
        }


        /// <summary>
        ///                     Returns a <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.
        ///                 </summary>
        /// <returns>
        ///                     A <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.
        ///                 </returns>
        /// <filterpriority>2</filterpriority>
        public override string ToString()
        {
            return Inner.ToString();
        }

        ///<summary>
        ///</summary>
        ///<param name="r"></param>
        public void Read(BinaryReader r)
        {
            _id.Read(r);
        }

        ///<summary>
        ///</summary>
        ///<param name="w"></param>
        public void Write(BinaryWriter w)
        {
            Inner.Write(w);
        }

        #region HierarchyId -> HierarchyId Operators

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator ==(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner == hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator !=(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner != hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator <(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner < hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator >(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner > hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator <=(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner <= hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator >=(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner >= hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        #endregion

        #region SqlHierarchyId -> HierarchyId  Operators

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator ==(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 == hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator !=(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 != hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator <(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 < hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator >(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 > hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator <=(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 <= hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator >=(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 >= hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        #endregion

        #region HierarchyId -> SqlHierarchyId Operators

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator ==(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner == hid2);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator !=(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner != hid2);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator <(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner < hid2);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator >(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner > hid2);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator <=(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner <= hid2);
            }
            return SqlBoolean.Null;
        }

        ///<summary>
        ///</summary>
        ///<param name="hid1"></param>
        ///<param name="hid2"></param>
        ///<returns></returns>
        public static SqlBoolean operator >=(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner >= hid2);
            }
            return SqlBoolean.Null;
        }

        #endregion
    }
}